/**
 * UploadService.java
 * 作者：IccBoY
 * 2017年6月4日 创建文件
 */
package com.iccboy.cupload.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.iccboy.cupload.engine.SysException;
import com.iccboy.cupload.util.DateUtil;
import com.iccboy.cupload.util.HttpUtil;
import com.iccboy.cupload.util.PropertyUtil;

/**
 **************************************************
 * 在此添加功能描述
 * 
 * @version 1.0.0
 * @author  iccboy
 **************************************************
 */
public class UploadService {

	/**
	 * LOG
	 */
	private static final Logger LOG = LoggerFactory.getLogger(UploadService.class);
	
	/**
	 * LIST
	 */
	public static final String LIST = "/list";

	/**
	 * FORM
	 */
	public static final String FORM = "/form";

	/**
	 * HANDLE
	 */
	public static final String HANDLE = "/handle";

	/**
	 * DOWNLOAD
	 */
	public static final String DOWNLOAD = "/download";

	/**
	 * DELETE
	 */
	public static final String DELETE = "/delete";

	/**
	 * UPLOAD_DIR
	 */
	public static final String UPLOAD_DIR = PropertyUtil.get("upload.path");

	/**
	 * 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
	 */
    private static final int MEMORY_THRESHOLD   = 1024 * 1024 * PropertyUtil.getInt("upload.memory.threshold");
    
    /**
     * 设置最大文件上传值
     */
    private static final int MAX_FILE_SIZE      = 1024 * 1024 * PropertyUtil.getInt("upload.max.file.size");
  
    /**
     * 设置最大请求值 (包含文件和表单数据)
     */
    private static final int MAX_REQUEST_SIZE   = 1024 * 1024 * PropertyUtil.getInt("upload.max.request.size");
	
	
	/**
	 * 写文件
	 * @param resp
	 * @param content
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	private static void write(HttpServletResponse resp, String content) {
		PrintWriter out = null;
		try {
			resp.setContentType("text/html;charset=utf-8");
			out = resp.getWriter();
			out.write(content);
		} catch (IOException e) {
			LOG.error("写错误", e);
		} finally {
			if(out != null) {
				out.flush();
				out.close();
			}
		}
	}
	
	/**
	 * show404 
	 * @param resp resp
	 * @throws IOException IOException
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	public void show404(HttpServletResponse resp) {
		resp.setStatus(404);
		write(resp, "您所访问的资源不存在！");
	}
	
	/**
	 * show500
	 * @param resp contextPath
	 * @param resp resp
	 * @throws IOException IOException
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	public void show500(HttpServletResponse resp, String msg) {
		resp.setStatus(500);
		write(resp, msg);
	}
	

	/**
	 * form
	 * @param contextPath contextPath
	 * @param resp resp
	 * @throws IOException IOException
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	public void form(String contextPath, HttpServletResponse resp)
			throws IOException {
		StringBuilder sb = new StringBuilder();
		String href1 = contextPath + LIST;
		sb.append("<html>");
		sb.append("<head><title>form</title></thead>");
		sb.append("<body>");
		sb.append("<b><a href='" + href1 + "'>点击</a>返回文件列表</b>");
		sb.append("<form action='handle' method='post' enctype='multipart/form-data' style='margin-top:20px;'>");
		sb.append("<input name='file' type='file'/><br>");
		sb.append("path:<input name='path' type='text'/><br>");
		sb.append("<input type='submit' value='上传'/><br>");
		sb.append("</form>");
		sb.append("</body>");
		sb.append("</html>");
		write(resp, sb.toString());
	}

	/**
	 * download
	 * @param file file
	 * @param resp resp
	 * @throws IOException IOException
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	public void download(File file, HttpServletResponse resp) throws Exception {
		String fileName = file.getName();
		LOG.info("fileName:" + fileName);
		resp.reset();
		
		resp.setContentType("application/octet-stream"); 
		resp.setHeader("Content-Type", "text/html; charset=utf-8");
		String encodingFilename = new String(fileName.getBytes("utf-8"), "iso8859-1");
		LOG.info("encodingFilename:" + encodingFilename);
		resp.setHeader("Content-disposition", "attachment;filename=\"" + encodingFilename + "\"");
		
		InputStream in = null;
		OutputStream out = null;
		try {
			in = new FileInputStream(file);
			out = resp.getOutputStream();
			byte[] b = new byte[1024];
			int n = 0;
			while ((n = in.read(b)) != -1) {
				out.write(b, 0, n);
			}
		} finally {
			if(in != null) {
				in.close();
			}
			if(out != null) {
				out.flush();
				out.close();
			}
		}
	}

	/**
	 * list
	 * @param contextPath contextPath
	 * @param basePath basePath
	 * @param resp resp
	 * @throws IOException IOException
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	public void list(String contextPath, String basePath,HttpServletRequest request,
			HttpServletResponse resp) {
		StringBuilder sb = new StringBuilder();
		String hrefu = contextPath + FORM;
		sb.append("<html>");
		sb.append("<head><title>list</title></thead>");
		sb.append("<body>");
		sb.append("<b>我要<a href='" + hrefu + "'>上传</a></b><br>");
		sb.append("<table border='1' style='border-collapse:collapse;width:100%;margin-top:20px;'>");
		sb.append("<thead>");
		sb.append("<tr>");
		sb.append("<th>#</th><th>File Name</th><th>Time</th><th>Operation</th>");
		sb.append("</tr>");
		sb.append("</thead>");
		sb.append("<tbody>");
		
		String path = request.getParameter("path");
		LOG.info("path:" + path);
		String absPath = null;
		if (path == null) {
			path = "";
		}
		
		File basePathFile = new File(basePath);
		
		if(!basePathFile.exists() || !basePathFile.isDirectory()) {
			basePathFile.mkdirs();
		}
		
		absPath = basePath + "/" + path;
		absPath = getPath(absPath);
		File file = new File(absPath);
		
		if(file.exists()) {
			File[] list = file.listFiles();
			LOG.info("basePath:" + basePath + "\tlist.size:"+ list.length);
			int len = list.length;
			int no = 1;
			for (int i = 0; i < len; i++) {
				File f = list[i];
				LOG.info(i + "\t" + f.getName());
				String filename = f.getName();
				String filepath = getPath(path + "/" + filename);
				String href1 = contextPath + DOWNLOAD + "?path=" + filepath;
				String href2 = contextPath + DELETE + "?path=" + filepath;
				String href3 = contextPath + LIST + "?path=" + filepath;
				sb.append("<tr>");
				sb.append("<td>");
				sb.append(no++);
				sb.append("</td>");
				sb.append("<td>");
				sb.append(filename);
				sb.append("</td>");
				sb.append("<td>");
				sb.append(DateUtil.getTimeStr(f.lastModified()));
				sb.append("</td>");
				sb.append("<td>");
				if(f.isDirectory()) {
					sb.append("<a href='");
					sb.append(href3);
					sb.append("'>进入</a>");
				} else {
					sb.append("<a href='");
					sb.append(href1);
					sb.append("'>下载</a> <a href='");
					sb.append(href2);
					sb.append("' onclick='return confirm(\"您确定要删除吗？\");'>删除</a>");
				}
				sb.append("</td>");
				sb.append("</tr>");
			}
			sb.append("</tbody>");
			sb.append("</table>");
			sb.append("</body>");
			sb.append("</html>");
			write(resp, sb.toString());
		}
	}

	/**
	 * getSuffix
	 * @return
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	public String getSuffix() {
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
		String suffix = sdf.format(date);
		return suffix;
	}
	
	/**
	 * handle
	 * @param contextPath contextPath
	 * @param basePath basePath
	 * @param request request
	 * @param response response
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 * @throws Exception 
	 */
	public void handle(String contextPath, String basePath, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		boolean isMultipart = ServletFileUpload.isMultipartContent(request);
		LOG.info("isMultipart:" + isMultipart);
		if (!isMultipart) {
			throw new SysException("请求类型错误");
		}
		DiskFileItemFactory factory = new DiskFileItemFactory();
		File repository = new File(System.getProperty("java.io.tmpdir"));
		LOG.info("repository:" + repository.getAbsolutePath());
		LOG.info("basePath:" + basePath);
		// 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
        factory.setSizeThreshold(MEMORY_THRESHOLD);
		factory.setRepository(repository);
		ServletFileUpload upload = new ServletFileUpload(factory);
		// 设置最大文件上传值
        upload.setFileSizeMax(MAX_FILE_SIZE);
        // 设置最大请求值 (包含文件和表单数据)
        upload.setSizeMax(MAX_REQUEST_SIZE);
		// 创建监听
		/*ProgressListener progressListener = new ProgressListener() {
			public void update(long pBytesRead, long pContentLength,
					int pItems) {
				LOG.debug("当前文件大小:" + pContentLength + "\t已经处理:"+ pBytesRead);
			}
		};
		upload.setProgressListener(progressListener);*/
		List<FileItem> items = null;
		items = upload.parseRequest(request);
		LOG.info("items size:" + items.size());
		Iterator<FileItem> ite = items.iterator();
		List<FileItem> fileItems = new ArrayList<FileItem>();
		String path = null;
		while (ite.hasNext()) {
			FileItem item = ite.next();
			if (item.isFormField()) {
				String fieldName = item.getFieldName();
				if("path".equals(fieldName) && path == null) {
					path = item.getString(); 
				} 
			} else {
				fileItems.add(item);
			}
		}
		boolean useUserPath = (path != null && !"".equals(path.trim()) && fileItems.size() == 1);
		if(fileItems.isEmpty()) {
			throw new SysException("没有文件上传");
		}
		for (FileItem item : fileItems) {
			// handle file
			String fieldName = item.getFieldName();
			String fileName = item.getName();
			fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);
			String contentType = item.getContentType();
			boolean isInMemory = item.isInMemory();
			long sizeInBytes = item.getSize();
			LOG.info(fieldName + "\t" + fileName + "\t" + contentType + "\t" + isInMemory + "\t" + sizeInBytes);
			String absPath = null;
			if(useUserPath) {
				absPath = basePath + "/" + path + "/" + fileName;
			} else {
				absPath = basePath + "/" + fileName + "_" + getSuffix();
			}
			absPath = getPath(absPath);
			
			File file = new File(absPath);
			
			if(!file.getParentFile().exists()) {
				file.getParentFile().mkdirs();
			}
			
			item.write(file);
		}
	}
	
	/**
	 * getPath
	 * @param path path
	 * @return path
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	private static String getPath(String path) {
		String absPath = path.replace("../", "./");
		absPath = absPath.replace("..\\", ".\\");
		absPath = absPath.replace("//", "/");
		absPath = absPath.replace("\\\\", "\\");
		absPath = absPath.replace("\\", File.separator);
		absPath = absPath.replace("\\\\", File.separator);
		return absPath;
	}
	
	/**
	 * 下载
	 * @param contextPath contextPath
	 * @param basePath basePath
	 * @param request request
	 * @param response response
	 * @throws IOException IOException
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	public void download(String contextPath, String basePath, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		String path = request.getParameter("path");
		LOG.info("path:" + path);
		if (path != null) {
			String absPath = basePath + "/" + path;
			absPath = getPath(absPath);
			File file = new File(absPath);
			if(file != null && file.isFile()) {
				download(file, response);
			} 
		} else {
			show404(response);
		}
	}
	
	/**
	 * 删除
	 * @param contextPath contextPath
	 * @param basePath basePath
	 * @param request request
	 * @param response response
	 * @throws IOException IOException
	 * @since 1.0.0
	 * @author iccboy 2017年6月5日 创建方法
	 */
	public void delete(String contextPath, String basePath, HttpServletRequest request,
			HttpServletResponse response) {
		String path = request.getParameter("path");
		LOG.info("path:" + path);
		if (path != null) {
			String absPath = basePath + "/" + path;
			absPath = getPath(absPath);
			File file = new File(absPath);
			
			if(file != null && file.isFile()) {
				file.delete();
			}
		} else {
			show404(response);
		}
	}
	
	/**
	 * 验证IP是否授权
	 * @throws SysException
	 * @since 1.0
	 * @author csr 2014-10-10 创建方法
	 */
	public void checkIpAddr(HttpServletRequest request) throws SysException {
		String clientIp = HttpUtil.getIpAddr(request);
		LOG.info("请求ip：" + clientIp);
		String allowIps = PropertyUtil.get("upload.ip");
		if (null != allowIps && !"".equals(allowIps.trim()) && !allowIps.contains(clientIp)) {
			String [] ips = allowIps.split(",");
			for (String ip : ips) {
				if(clientIp.contains(ip)) {
					return;
				}
			}
			LOG.info(clientIp + "验证失败！");
			throw new SysException("文件上传ip地址未授权");
		}
	}
	
}
